<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Denzel - 浅尝 Web Components </title>
    <meta name="description" content="前端笔记">
    <meta name="keywords" content="web前端,HTML5,CSS3,Javascript,SVG,canvas">
  <meta name="anthor" content="Denzel">
  <meta name="theme-color" content="#3eaf7c">
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="msapplication-TileImage" content="/icons/msapplication-icon-144x144.png">
  <meta name="msapplication-TileColor" content="#000000">
  <link rel="icon" href="/blog/vuepress/logo.png">
  <link rel="manifest" href="/blog/vuepress/manifest.json">
  <link rel="apple-touch-icon" href="/blog/vuepress/icons/apple-touch-icon-152x152.png">
  <link rel="mask-icon" href="/blog/vuepress/icons/safari-pinned-tab.svg" color="#3eaf7c">
    
    <link rel="preload" href="/blog/vuepress/assets/css/0.styles.d4f29607.css" as="style"><link rel="preload" href="/blog/vuepress/assets/js/app.15770e34.js" as="script"><link rel="preload" href="/blog/vuepress/assets/js/40.bc709734.js" as="script"><link rel="preload" href="/blog/vuepress/assets/js/65.923a8d15.js" as="script"><link rel="prefetch" href="/blog/vuepress/assets/js/46.59c0ece7.js"><link rel="prefetch" href="/blog/vuepress/assets/js/1.05e4270f.js"><link rel="prefetch" href="/blog/vuepress/assets/js/2.ace739f7.js"><link rel="prefetch" href="/blog/vuepress/assets/js/3.636e5a79.js"><link rel="prefetch" href="/blog/vuepress/assets/js/4.7a55aa4b.js"><link rel="prefetch" href="/blog/vuepress/assets/js/5.b56b0573.js"><link rel="prefetch" href="/blog/vuepress/assets/js/6.e9a3fbc8.js"><link rel="prefetch" href="/blog/vuepress/assets/js/7.aa46c47d.js"><link rel="prefetch" href="/blog/vuepress/assets/js/8.a9288ac6.js"><link rel="prefetch" href="/blog/vuepress/assets/js/9.b9f8dec6.js"><link rel="prefetch" href="/blog/vuepress/assets/js/10.3e2ea470.js"><link rel="prefetch" href="/blog/vuepress/assets/js/11.4d115f98.js"><link rel="prefetch" href="/blog/vuepress/assets/js/12.c97cf4d0.js"><link rel="prefetch" href="/blog/vuepress/assets/js/13.1e0cf594.js"><link rel="prefetch" href="/blog/vuepress/assets/js/14.bab9026a.js"><link rel="prefetch" href="/blog/vuepress/assets/js/15.1a24cffe.js"><link rel="prefetch" href="/blog/vuepress/assets/js/16.e9995850.js"><link rel="prefetch" href="/blog/vuepress/assets/js/17.86a1f7d2.js"><link rel="prefetch" href="/blog/vuepress/assets/js/18.c32400c4.js"><link rel="prefetch" href="/blog/vuepress/assets/js/19.9f99887c.js"><link rel="prefetch" href="/blog/vuepress/assets/js/20.82f0c2f9.js"><link rel="prefetch" href="/blog/vuepress/assets/js/21.7f5c223d.js"><link rel="prefetch" href="/blog/vuepress/assets/js/22.acf9934c.js"><link rel="prefetch" href="/blog/vuepress/assets/js/23.20d8e817.js"><link rel="prefetch" href="/blog/vuepress/assets/js/24.acf27379.js"><link rel="prefetch" href="/blog/vuepress/assets/js/25.20165510.js"><link rel="prefetch" href="/blog/vuepress/assets/js/26.83a2ddc3.js"><link rel="prefetch" href="/blog/vuepress/assets/js/27.33276a04.js"><link rel="prefetch" href="/blog/vuepress/assets/js/28.e320efab.js"><link rel="prefetch" href="/blog/vuepress/assets/js/29.3fd92c30.js"><link rel="prefetch" href="/blog/vuepress/assets/js/30.2bc84d43.js"><link rel="prefetch" href="/blog/vuepress/assets/js/31.d70ed500.js"><link rel="prefetch" href="/blog/vuepress/assets/js/32.e7a0f8e1.js"><link rel="prefetch" href="/blog/vuepress/assets/js/33.c48ee70b.js"><link rel="prefetch" href="/blog/vuepress/assets/js/34.8ad03d1b.js"><link rel="prefetch" href="/blog/vuepress/assets/js/35.5bdb3994.js"><link rel="prefetch" href="/blog/vuepress/assets/js/36.a25f926e.js"><link rel="prefetch" href="/blog/vuepress/assets/js/37.7515cfbb.js"><link rel="prefetch" href="/blog/vuepress/assets/js/38.99347371.js"><link rel="prefetch" href="/blog/vuepress/assets/js/39.3481fc62.js"><link rel="prefetch" href="/blog/vuepress/assets/js/41.e1e0a3bf.js"><link rel="prefetch" href="/blog/vuepress/assets/js/42.762b61b0.js"><link rel="prefetch" href="/blog/vuepress/assets/js/43.8f0b2d6c.js"><link rel="prefetch" href="/blog/vuepress/assets/js/44.2a0a4ee0.js"><link rel="prefetch" href="/blog/vuepress/assets/js/45.baf7ed60.js"><link rel="prefetch" href="/blog/vuepress/assets/js/47.11a8720a.js"><link rel="prefetch" href="/blog/vuepress/assets/js/48.e0bf8b54.js"><link rel="prefetch" href="/blog/vuepress/assets/js/49.c96c4011.js"><link rel="prefetch" href="/blog/vuepress/assets/js/50.31189263.js"><link rel="prefetch" href="/blog/vuepress/assets/js/51.766a8294.js"><link rel="prefetch" href="/blog/vuepress/assets/js/52.26b971fd.js"><link rel="prefetch" href="/blog/vuepress/assets/js/53.0cfbd708.js"><link rel="prefetch" href="/blog/vuepress/assets/js/54.bdc15c87.js"><link rel="prefetch" href="/blog/vuepress/assets/js/55.fea285a9.js"><link rel="prefetch" href="/blog/vuepress/assets/js/56.e030a6b2.js"><link rel="prefetch" href="/blog/vuepress/assets/js/57.bd168a1e.js"><link rel="prefetch" href="/blog/vuepress/assets/js/58.636557fa.js"><link rel="prefetch" href="/blog/vuepress/assets/js/59.5d1d0ce6.js"><link rel="prefetch" href="/blog/vuepress/assets/js/60.0b779f92.js"><link rel="prefetch" href="/blog/vuepress/assets/js/61.4ec3f2eb.js"><link rel="prefetch" href="/blog/vuepress/assets/js/62.8f70f114.js"><link rel="prefetch" href="/blog/vuepress/assets/js/63.9ac2b61d.js"><link rel="prefetch" href="/blog/vuepress/assets/js/64.c028fad5.js"><link rel="prefetch" href="/blog/vuepress/assets/js/66.73db652e.js"><link rel="prefetch" href="/blog/vuepress/assets/js/67.1a91446d.js"><link rel="prefetch" href="/blog/vuepress/assets/js/68.e5a12a42.js"><link rel="prefetch" href="/blog/vuepress/assets/js/69.6ddcb882.js"><link rel="prefetch" href="/blog/vuepress/assets/js/70.ceb70d85.js"><link rel="prefetch" href="/blog/vuepress/assets/js/71.a804ef40.js"><link rel="prefetch" href="/blog/vuepress/assets/js/72.f1c4098c.js"><link rel="prefetch" href="/blog/vuepress/assets/js/73.2f28b842.js"><link rel="prefetch" href="/blog/vuepress/assets/js/74.571c2ab1.js"><link rel="prefetch" href="/blog/vuepress/assets/js/75.f365c2a1.js"><link rel="prefetch" href="/blog/vuepress/assets/js/76.0916a3cd.js"><link rel="prefetch" href="/blog/vuepress/assets/js/77.469aed17.js"><link rel="prefetch" href="/blog/vuepress/assets/js/78.02cdd59d.js"><link rel="prefetch" href="/blog/vuepress/assets/js/79.44d7f95c.js"><link rel="prefetch" href="/blog/vuepress/assets/js/80.4ca072f1.js"><link rel="prefetch" href="/blog/vuepress/assets/js/81.d9239a49.js"><link rel="prefetch" href="/blog/vuepress/assets/js/82.b09096b2.js"><link rel="prefetch" href="/blog/vuepress/assets/js/83.f4bc8a9b.js"><link rel="prefetch" href="/blog/vuepress/assets/js/84.1ffe6969.js"><link rel="prefetch" href="/blog/vuepress/assets/js/85.44e980f9.js"><link rel="prefetch" href="/blog/vuepress/assets/js/86.c8579ac0.js"><link rel="prefetch" href="/blog/vuepress/assets/js/87.cd49f324.js"><link rel="prefetch" href="/blog/vuepress/assets/js/88.3851b6ff.js"><link rel="prefetch" href="/blog/vuepress/assets/js/89.b05297be.js"><link rel="prefetch" href="/blog/vuepress/assets/js/90.c1e3263e.js"><link rel="prefetch" href="/blog/vuepress/assets/js/91.b493a1bb.js"><link rel="prefetch" href="/blog/vuepress/assets/js/92.80ab06fd.js"><link rel="prefetch" href="/blog/vuepress/assets/js/93.c6ca00b3.js"><link rel="prefetch" href="/blog/vuepress/assets/js/94.e993d310.js"><link rel="prefetch" href="/blog/vuepress/assets/js/95.8e19a7ed.js">
    <link rel="stylesheet" href="/blog/vuepress/assets/css/0.styles.d4f29607.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div><a href="/blog/vuepress/" class="home-link router-link-active"><img src="/blog/vuepress/hero.png" class="logo"><span class="site-name can-hide">
      Denzel
    </span></a><div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""><!----></div><nav class="nav-links can-hide"><div class="nav-item"><a href="/blog/vuepress/js/" class="nav-link">JS</a></div><div class="nav-item"><a href="/blog/vuepress/css/" class="nav-link">CSS</a></div><div class="nav-item"><a href="/blog/vuepress/html/" class="nav-link router-link-active">HTML</a></div><div class="nav-item"><a href="/blog/vuepress/else/" class="nav-link">其他</a></div><div class="nav-item"><a href="/blog/vuepress/study/" class="nav-link">study</a></div><a href="https://github.com/xiaotianxia" target="_blank" rel="noopener noreferrer" class="repo-link">
    GitHub
    <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></nav></div></header><div class="sidebar-mask"></div><div class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/blog/vuepress/js/" class="nav-link">JS</a></div><div class="nav-item"><a href="/blog/vuepress/css/" class="nav-link">CSS</a></div><div class="nav-item"><a href="/blog/vuepress/html/" class="nav-link router-link-active">HTML</a></div><div class="nav-item"><a href="/blog/vuepress/else/" class="nav-link">其他</a></div><div class="nav-item"><a href="/blog/vuepress/study/" class="nav-link">study</a></div><a href="https://github.com/xiaotianxia" target="_blank" rel="noopener noreferrer" class="repo-link">
    GitHub
    <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></nav><ul class="sidebar-links"><li><div class="sidebar-group first"><p class="sidebar-heading open"><span>HTML</span><!----></p><ul class="sidebar-group-items"><li><a href="/blog/vuepress/html/" class="sidebar-link">目录</a></li><li><a href="/blog/vuepress/html/web_components.html" class="active sidebar-link">浅尝 Web Components </a></li><li><a href="/blog/vuepress/html/canvas_video_green_screen_cutout.html" class="sidebar-link">canvas像素点操作 —— 视频绿幕抠图</a></li><li><a href="/blog/vuepress/html/canvas_pixel_pick.html" class="sidebar-link">canvas像素点获取 —— 拾色器、放大器</a></li><li><a href="/blog/vuepress/html/dialog_element.html" class="sidebar-link">HTML5.2新标签 —— dialog </a></li></ul></div></li></ul></div><div class="page"><div class="content"><h1 id="浅尝-web-components"><a href="#浅尝-web-components" aria-hidden="true" class="header-anchor">#</a> 浅尝 Web Components</h1><p><a href="https://denzel.netlify.com/html/web_components.html" target="_blank" rel="noopener noreferrer">原文地址</a></p><h2 id="前言"><a href="#前言" aria-hidden="true" class="header-anchor">#</a> 前言</h2><p>Web Components涉及到的内容还是很多的，每一块都有很多东西可以讲，国外的好多大佬已经产出了好多优秀的文章。
本文照常只是简单了解大致内容而不进入深究，了解且会用即可，浅尝辄止。</p><h2 id="简介"><a href="#简介" aria-hidden="true" class="header-anchor">#</a> 简介</h2><p>Web Components本身<strong>不是一个</strong>规范，而是由W3C提出的另外4个规范的合集。这四个规范是：</p><ul><li><a href="#html-template"><strong>HTML Template</strong></a></li><li><a href="#html-imports"><strong>HTML Imports</strong></a></li><li><a href="#shadow-dom"><strong>Shadow DOM</strong></a></li><li><a href="#custom-elements"><strong>Custom Elements</strong></a></li></ul><p>下面我们走马观花，简单了解一个这四个东西。</p><h2 id="html-template"><a href="#html-template" aria-hidden="true" class="header-anchor">#</a> HTML Template</h2><p>之前的页面开发经常的一个做法是把模板放在一个script标签或者隐藏的div中，用的时候通过innerHTML取出，塞进数据，
然后放回页面显示。现在我们可以通过&lt;template&gt;标签存放了。就像这样：</p><pre class="language-html"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>mytemplate<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span><span class="token punctuation">&quot;</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>great image<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
  	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>comment<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">&gt;</span></span>
</code></pre><h3 id="特性检测"><a href="#特性检测" aria-hidden="true" class="header-anchor">#</a> 特性检测</h3><p>要特性检测 &lt;template&gt;，可以创建一个 template 元素并检查它是否拥有 content 属性：</p><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">supportsTemplate</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">return</span> <span class="token string">'content'</span> <span class="token keyword">in</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'template'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">supportsTemplate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  	<span class="token comment">// 检测通过！</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
  	<span class="token comment">// 使用旧的模板技术或库。</span>
<span class="token punctuation">}</span>
</code></pre><h3 id="激活模板"><a href="#激活模板" aria-hidden="true" class="header-anchor">#</a> 激活模板</h3><p>激活模板，即渲染出模板里面的内容。激活模板最简单的方法就是使用
document.importNode() 对模板的 .content 进行深拷贝。
.content 为只读属性，关联一个包含模板内容的 DocumentFragment。</p><pre class="language-js"><code><span class="token keyword">var</span> t <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'#mytemplate'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 在运行时填充 src。</span>
t<span class="token punctuation">.</span>content<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'img'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>src <span class="token operator">=</span> <span class="token string">'logo.png'</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> clone <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">importNode</span><span class="token punctuation">(</span>t<span class="token punctuation">.</span>content<span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
document<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>clone<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><h3 id="特点"><a href="#特点" aria-hidden="true" class="header-anchor">#</a> 特点</h3><p>用 &lt;template&gt; 来包裹内容为我们提供了几个重要属性：</p><ul><li><p>它的内容在激活之前一直处于惰性状态。本质上，这些标记就是隐藏的 DOM，它们不会被渲染。</p></li><li><p>处于模板中的内容不会有副作用。脚本不会运行，图片不会加载，音频不会播放，...直到模板被使用。</p></li><li><p>内容不在文档中。在主页面使用 document.getElementById() 或 querySelector() 不会返回模板的子节点。</p></li><li><p>模板能够被放置在任何位置，包括 &lt;head&gt;，&lt;body&gt;，或 &lt;frameset&gt;，并且任何能够出现在以上元素中的内容都可以放到模板中。
注意，&quot;任何位置&quot;意味着 &lt;template&gt; 能够安全的出现在 HTML 解析器不允许出现的位置...
几乎可以作为任何内容模型的子节点， 它也可以作为 &lt;table&gt; 或 &lt;select&gt; 的子元素。</p></li></ul><h3 id="推荐阅读"><a href="#推荐阅读" aria-hidden="true" class="header-anchor">#</a> 推荐阅读</h3><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_templates_and_slots" target="_blank" rel="noopener noreferrer">Using templates and slots</a></li><li><a href="https://www.html5rocks.com/en/tutorials/webcomponents/template/" target="_blank" rel="noopener noreferrer">HTML's New Template Tag - standardizing client-side templating</a></li></ul><h2 id="html-imports"><a href="#html-imports" aria-hidden="true" class="header-anchor">#</a> HTML Imports</h2><p>之前在页面引入另一个页面或片段往往是通过iframe或者ajax异步加载，而现在我们可以这样做：</p><p>在head中引入</p><pre class="language-html"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">&gt;</span></span>
	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>import<span class="token punctuation">&quot;</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>/path/to/imports/stuff.html<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>js中获取</p><pre class="language-js"><code><span class="token keyword">var</span> content <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'link[rel=&quot;import&quot;]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token keyword">import</span><span class="token punctuation">;</span>
</code></pre><h3 id="特性检测-2"><a href="#特性检测-2" aria-hidden="true" class="header-anchor">#</a> 特性检测</h3><p>要检测浏览器是否支持导入，可验证 &lt;link&gt; 元素上是否存在 import：</p><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">supportsImports</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">return</span> <span class="token string">'import'</span> <span class="token keyword">in</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'link'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">supportsImports</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  	<span class="token comment">// 支持导入</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
  	<span class="token comment">// 使用其他方法加载文件</span>
<span class="token punctuation">}</span>
</code></pre><h3 id="推荐阅读-2"><a href="#推荐阅读-2" aria-hidden="true" class="header-anchor">#</a> 推荐阅读</h3><ul><li><a href="https://w3c.github.io/webcomponents/spec/imports/" target="_blank" rel="noopener noreferrer">HTML Imports</a></li><li><a href="https://www.html5rocks.com/en/tutorials/webcomponents/imports/" target="_blank" rel="noopener noreferrer">HTML Imports - #include for the web</a></li></ul><h2 id="shadow-dom"><a href="#shadow-dom" aria-hidden="true" class="header-anchor">#</a> Shadow DOM</h2><p>首先需要设置一下：打开开发者工具，f1打开设置(或右上角三个点)，然后勾上Show user agent shadow DOM ——
<img src="http://pn4meizzc.bkt.clouddn.com/component005.png" alt></p><p>然后再看下，video标签是这样的 ——
<img src="http://pn4meizzc.bkt.clouddn.com/component006.png" alt></p><p>甚至一个普通的input ——
<img src="http://pn4meizzc.bkt.clouddn.com/component008.png" alt></p><p>之前被隐藏掉的DOM部分就是shadow DOM。顾名思义，它是其宿主元素的影子，通常用来封装组件的内部结构。</p><p>所以像video、audio甚至input都是用简单的元素封装的组件。</p><p>这让我想到，我们是不是可以通过修改元素里面的shadow DOM的样式来改变该元素的样式呢？
答案是 —— 是的，但也<strong>不完全</strong>是...</p><p><img src="http://pn4meizzc.bkt.clouddn.com/component009.png" alt></p><p>从上图audio标签的结构和浏览器默认样式可以看到，我们可以像这样修改对应的样式：</p><pre class="language-css"><code><span class="token selector">audio::-webkit-media-controls</span> <span class="token punctuation">{</span>
	...
<span class="token punctuation">}</span>
</code></pre><p>就像通过::-webkit-scrollbar改造浏览的滚动条样式那样，</p><p>于是，默认的audio样式(新版chrome)——</p><p><img src="http://pn4meizzc.bkt.clouddn.com/componet003.png" alt="small"></p><p>经过改造后，可以变成这样——</p><p><img src="http://pn4meizzc.bkt.clouddn.com/component004.png" alt="small"></p><div class="warning custom-block"><p class="custom-block-title">WARNING</p><p>然而并不是所有样式都可以这样覆盖改造，像pseudo=&quot;-internal-media-controls-loading-panel&quot;这样以&quot;-internal-&quot;开头的是不可以的。
所以这个做法还是有很大局限性的。</p><p>这是我试出来的，并没发现相关标准或依据...😓</p></div><div class="codepen-wrapper"><a href="https://codepen.io/_tianxia/pen/gdEmbj" target="_blank" class="el-button el-button--primary is-plain">show in codepen <i class="el-icon-d-arrow-right"></i></a></div><p>这种做法自认为只适合拿来玩玩而已，不适合投入到项目开发中去。一来是因为其局限性太大，二来谁知道啥时候浏览器升级，这些标签的内部结构就又变化了呢，
最重要的是shadow DOM是为web Components而生的，与Custom Elements一起是web Components的重要组成部分，并非用于此“旁门左道”😆。</p><h3 id="推荐阅读-3"><a href="#推荐阅读-3" aria-hidden="true" class="header-anchor">#</a> 推荐阅读</h3><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM" target="_blank" rel="noopener noreferrer">Using shadow DOM</a></li><li><a href="https://www.html5rocks.com/en/tutorials/webcomponents/shadowdom/" target="_blank" rel="noopener noreferrer">Shadow DOM 101</a></li><li><a href="https://www.html5rocks.com/zh/tutorials/webcomponents/shadowdom-201/" target="_blank" rel="noopener noreferrer">Shadow DOM 201 - CSS and Styling</a></li><li><a href="https://www.html5rocks.com/zh/tutorials/webcomponents/shadowdom-301/" target="_blank" rel="noopener noreferrer">Shadow DOM 301 - Advanced Concepts &amp; DOM APIs</a></li></ul><h2 id="custom-elements"><a href="#custom-elements" aria-hidden="true" class="header-anchor">#</a> Custom Elements</h2><p>自定义元素，首先有个硬性规定，自定义元素的命名中必须要有中划线“-”，否则便是未知元素了。</p><p>自定义元素分为两种 ——</p><ul><li><a href="#%E8%87%AA%E4%B8%BB%E8%87%AA%E5%AE%9A%E4%B9%89%E5%85%83%E7%B4%A0-autonomous-custom-elements"><strong>自主自定义元素</strong></a></li><li><a href="#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%86%85%E7%BD%AE%E5%85%83%E7%B4%A0-customized-built-in-elements"><strong>自定义内置元素</strong></a></li></ul><h3 id="自特性主自定义元素-autonomous-custom-elements"><a href="#自特性主自定义元素-autonomous-custom-elements" aria-hidden="true" class="header-anchor">#</a> 自特性主自定义元素(Autonomous custom elements)</h3><p>不具备任何已有元素的，其样式和行为完全自定义，如我们要定义一个这样的元素：</p><pre class="language-html"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>flag-icon</span> <span class="token attr-name">country</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>cn<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>flag-icon</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>通过给属性country赋值来显示对应的国旗。</p><p>js的基本结构是这样的</p><pre class="language-js"><code><span class="token keyword">class</span> <span class="token class-name">FlagIcon</span> <span class="token keyword">extends</span> <span class="token class-name">HTMLElement</span> <span class="token punctuation">{</span>
  	<span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    	<span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    	<span class="token keyword">this</span><span class="token punctuation">.</span>_countryCode <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
  	<span class="token punctuation">}</span>

  	<span class="token keyword">static</span> <span class="token keyword">get</span> <span class="token function">observedAttributes</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">[</span><span class="token string">&quot;country&quot;</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

  	<span class="token function">attributeChangedCallback</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> oldValue<span class="token punctuation">,</span> newValue<span class="token punctuation">)</span> <span class="token punctuation">{</span>
	    <span class="token comment">// name will always be &quot;country&quot; due to observedAttributes</span>
	    <span class="token keyword">this</span><span class="token punctuation">.</span>_countryCode <span class="token operator">=</span> newValue<span class="token punctuation">;</span>
	    <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">_updateRendering</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  	<span class="token punctuation">}</span>

  	<span class="token function">connectedCallback</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    	<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">_updateRendering</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  	<span class="token punctuation">}</span>

  	<span class="token keyword">get</span> <span class="token function">country</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    	<span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_countryCode<span class="token punctuation">;</span>
  	<span class="token punctuation">}</span>

  	<span class="token keyword">set</span> <span class="token function">country</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    	<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">&quot;country&quot;</span><span class="token punctuation">,</span> v<span class="token punctuation">)</span><span class="token punctuation">;</span>
  	<span class="token punctuation">}</span>

  	<span class="token function">_updateRendering</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	    <span class="token comment">//...</span>
  	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment">//全局注册该元素</span>
customElements<span class="token punctuation">.</span><span class="token function">define</span><span class="token punctuation">(</span><span class="token string">&quot;flag-icon&quot;</span><span class="token punctuation">,</span> FlagIcon<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>注册后，也通过js创建该元素</p><pre class="language-js"><code><span class="token keyword">const</span> flagIcon <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">&quot;flag-icon&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
flagIcon<span class="token punctuation">.</span>country <span class="token operator">=</span> <span class="token string">&quot;cn&quot;</span><span class="token punctuation">;</span>
document<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>flagIcon<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><h3 id="自定义内置元素-customized-built-in-elements"><a href="#自定义内置元素-customized-built-in-elements" aria-hidden="true" class="header-anchor">#</a> 自定义内置元素(Customized built-in elements)</h3><p>继承自已有元素，拥有已有元素的所有特性。</p><p>比如我们自定义一个按钮，集成普通按钮所有的特性，但是当点击的时候会有一个动效，就可以这么做 ——</p><pre class="language-js"><code><span class="token keyword">class</span> <span class="token class-name">PlasticButton</span> <span class="token keyword">extends</span> <span class="token class-name">HTMLButtonElement</span> <span class="token punctuation">{</span>
  	<span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    	<span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    	<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">&quot;click&quot;</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      		<span class="token comment">// 动效逻辑</span>
    	<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>不同的是，注册时要加上一个参数</p><pre class="language-js"><code>customElements<span class="token punctuation">.</span><span class="token function">define</span><span class="token punctuation">(</span><span class="token string">&quot;plastic-button&quot;</span><span class="token punctuation">,</span> PlasticButton<span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token keyword">extends</span><span class="token punctuation">:</span> <span class="token string">&quot;button&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>使用时也稍有不同</p><pre class="language-html"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">is</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">&quot;</span>plastic-button<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>点我!<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>通过js定义元素，则是这样</p><pre class="language-js"><code><span class="token keyword">const</span> plasticButton <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">&quot;button&quot;</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> is<span class="token punctuation">:</span> <span class="token string">&quot;plastic-button&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
plasticButton<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token string">&quot;点我!&quot;</span><span class="token punctuation">;</span>
document<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>flagIcon<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><h3 id="生命周期"><a href="#生命周期" aria-hidden="true" class="header-anchor">#</a> 生命周期</h3><p>用过Vue、React等框架的同学对生命周期应该不陌生。同样，自定义元素有4个生命周期：</p><h4 id="connectedcallback"><a href="#connectedcallback" aria-hidden="true" class="header-anchor">#</a> connectedCallback</h4><p>元素首次被插入文档DOM时触发</p><h4 id="disconnectedcallback"><a href="#disconnectedcallback" aria-hidden="true" class="header-anchor">#</a> disconnectedCallback</h4><p>元素从文档DOM中删除时触发</p><h4 id="adoptedcallback"><a href="#adoptedcallback" aria-hidden="true" class="header-anchor">#</a> adoptedCallback</h4><p>元素被移动到新的文档时触发</p><h4 id="attributechangedcallback"><a href="#attributechangedcallback" aria-hidden="true" class="header-anchor">#</a> attributeChangedCallback</h4><p>元素增加、删除、修改自身属性时触发</p><h3 id="推荐阅读-4"><a href="#推荐阅读-4" aria-hidden="true" class="header-anchor">#</a> 推荐阅读</h3><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements" target="_blank" rel="noopener noreferrer">Using custom elements</a></li><li><a href="https://www.html5rocks.com/en/tutorials/webcomponents/customelements/" target="_blank" rel="noopener noreferrer">Custom Elements - defining new elements in HTML</a></li></ul><h3 id="来一个demo"><a href="#来一个demo" aria-hidden="true" class="header-anchor">#</a> 来一个demo</h3><p>评分组件相信大家都司空见惯了。照葫芦画瓢，我用原生js写了一个Web Components 版的，简单实现了该组件的基本功能。</p><p>demo截图：</p><p><img src="http://pn4meizzc.bkt.clouddn.com/component001.gif" alt></p><p>demo地址：
<div class="codepen-wrapper"><a href="https://codepen.io/_tianxia/pen/mGZOOG/" target="_blank" class="el-button el-button--primary is-plain">show in codepen <i class="el-icon-d-arrow-right"></i></a></div></p><h2 id="推荐阅读-5"><a href="#推荐阅读-5" aria-hidden="true" class="header-anchor">#</a> 推荐阅读</h2><ul><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/Web_Components" target="_blank" rel="noopener noreferrer">Web Components</a></li><li><a href="https://www.webcomponents.org/" target="_blank" rel="noopener noreferrer">https://www.webcomponents.org/</a></li></ul><h2 id="总结"><a href="#总结" aria-hidden="true" class="header-anchor">#</a> 总结</h2><p>Web Components 为前端组件化提供了解决方案，但用惯了Vue这样的框架，还是会发现Web Components 的问题，
比如</p><ul><li>浏览器的支持</li><li>对样式局部作用域的处理，往往js中包着一堆的CSS样式，略显臃肿</li><li>父子、兄弟组件的通信问题</li><li>属性都是字符串，需要额外的代码做转换和兼容</li><li>没有数据驱动，基本全是DOM操作</li></ul><p>这些问题貌似都没有给出具体的解决方案，但是目前的好多优秀的polyfill已经做到很好了。</p><p>个人愚见，望大佬指点！🙏</p></div><div class="wx-wrapper" data-v-0de8a4bf><p class="wx-txt" data-v-0de8a4bf>欢迎关注</p><img src="http://pn4meizzc.bkt.clouddn.com/wxqr.png" class="wx-img" data-v-0de8a4bf></div><div class="beg-wrapper" data-v-1f1af99c><a href="javascript:;" class="btn show" data-v-1f1af99c><img src="http://pn4meizzc.bkt.clouddn.com/blogshang_log.png" data-v-1f1af99c></a><p class="shang-txt" data-v-1f1af99c>赏不赏，看您心情</p><img src="http://pn4meizzc.bkt.clouddn.com/blogWechatIMG35.jpeg" class="shangImg" data-v-1f1af99c></div><!----><div class="content page-nav"><p class="inner"><span class="prev">
        ← <a href="/blog/vuepress/html/" class="prev router-link-active">
          目录
        </a></span><span class="next"><a href="/blog/vuepress/html/canvas_video_green_screen_cutout.html">
          canvas像素点操作 —— 视频绿幕抠图
        </a> →
      </span></p></div></div></div></div>
    <script src="/blog/vuepress/assets/js/40.bc709734.js" defer></script><script src="/blog/vuepress/assets/js/65.923a8d15.js" defer></script><script src="/blog/vuepress/assets/js/app.15770e34.js" defer></script>
  </body>
</html>
